package com.domainlanguage.money;

import static com.domainlanguage.TimeAndMoneyTypes.DURATION;
import static com.domainlanguage.TimeAndMoneyTypes.MONEY;
import static com.domainlanguage.internal.FieldReaderUtils.*;
import com.domainlanguage.time.Duration;
import com.domainlanguage.time.ImmutableCompositeUserType;
import com.domainlanguage.time.TimeRate;
import org.hibernate.HibernateException;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.type.Type;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Currency;

/**
 *
 */
public class MoneyTimeRateType extends ImmutableCompositeUserType {
    private static final String[] PROPERTY_NAMES = {"money", "unit"};
    private static final Type[] PROPERTY_TYPES = {MONEY, DURATION};

    @Override
    public String[] getPropertyNames() {
        return PROPERTY_NAMES;
    }

    @Override
    public Type[] getPropertyTypes() {
        return PROPERTY_TYPES;
    }

    @Override
    public Object getPropertyValue(final Object component, final int property) throws HibernateException {
        if (component == null) {
            return null;
        }

        final MoneyTimeRate moneyTimeRate = (MoneyTimeRate) component;
        final TimeRate timeRate = readTimeRate(moneyTimeRate);
        switch (property) {
            case 0:
                final Currency currency = readCurrency(moneyTimeRate);
                return Money.valueOf(readQuantity(timeRate), currency);
            case 1:
                return readUnit(timeRate);
            default:
                throw new HibernateException("Property index " + property + " is out of bounds. " +
                        "Properties are: " + Arrays.toString(getPropertyNames()));
        }
    }

    @Override
    public Class returnedClass() {
        return MoneyTimeRate.class;
    }

    @Override
    public Object nullSafeGet(final ResultSet rs, final String[] names, final SessionImplementor session, final Object owner) throws HibernateException, SQLException {
        final Money money = (Money) MONEY.nullSafeGet(rs, names[0], session, owner);
        final Duration duration = (Duration) DURATION.nullSafeGet(rs, names[1]);

        if (money == null || duration == null) {
            return null;
        }

        return new MoneyTimeRate(money, duration);
    }

    @Override
    public void nullSafeSet(final PreparedStatement st, final Object value, final int index, final SessionImplementor session) throws HibernateException, SQLException {
        final MoneyTimeRate moneyTimeRate = (MoneyTimeRate) value;

        final Money money;
        final Duration duration;

        if (moneyTimeRate == null) {
            money = null;
            duration = null;
        } else {
            final Currency currency = readCurrency(moneyTimeRate);
            final TimeRate timeRate = readTimeRate(moneyTimeRate);

            money = Money.valueOf(readQuantity(timeRate), currency);
            duration = readUnit(timeRate);
        }

        MONEY.nullSafeSet(st, money,  index, session);
        DURATION.nullSafeSet(st, duration, index + 2, session); // MONEY uses two columns
    }

}
